<template>
  <div class="page page-web-crawler">
    <h4 class="text-center">网络爬虫</h4>
    <div class="page-body row">
      <div class="col-6">
        <fieldset>
          <legend>参数配置</legend>
          <div class="fieldset-content">
            <div class="main">
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">保存位置</div>
                <div class="row-flex-main"><input class="form-control" type="text" v-model="toPath" /></div>
                <div class="row-flex-suffix"><x-button @click="setToPath">选择...</x-button></div>
              </div>
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">自动保存</div>
                <div class="row-flex-main">
                  <div class="form-check form-check-inline">
                    <input class="form-check-input" type="checkbox" v-model="autoSave">
                    <label class="form-check-label">是</label>
                  </div>
                </div>
              </div>
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">资源类型</div>
                <div class="row-flex-main">
                  <div class="form-check form-check-inline" v-for="(v,i) in resourcesTypeList" :key="i">
                    <input class="form-check-input" type="checkbox" :value="v.value" v-model="resourcesType">
                    <label class="form-check-label">{{v.label}}</label>
                  </div>
                </div>
              </div>
              <div class="row-flex" align="center">
                <div class="row-flex-prefix">网址模板</div>
                <div class="row-flex-main"><input class="form-control" v-model="urlTemplate" placeholder="请输入内容，变量用${}标识，如${1}" /></div>
              </div>
              <div class="row-flex" align="center" v-if="variableArr.length">
                <div class="row-flex-prefix">变量字典</div>
                <div class="row-flex-main"></div>
              </div>
              <div class="row-flex" v-for="(v,i) in variableArr" :key="i">
                <div class="row-flex-prefix text-grey">{{v}}</div>
                <div class="row-flex-main">
                  <input class="form-control" v-model="variableDict[i]" placeholder="请输入变量字典，各项用逗号(,)分隔" />
                </div>
              </div>
            </div>
            <div class="bottom">
              <div class="btn-wrapper">
                <x-button type="primary" block @click="clickHandler">处 理</x-button>
              </div>
            </div>
          </div>
        </fieldset>
      </div>
      <div class="col-6">
        <fieldset>
          <legend>处理结果</legend>
          <div class="table-header" :style="thStyles">
            <table>
              <tbody>
                <tr>
                  <th v-for="(v,i) in tableHeaders" :key="`th_${i}`" :style="{width: v.width}">{{v.label}}</th>
                </tr>
              </tbody>
            </table>
          </div>
          <div class="table-body">
            <table ref="table">
              <tbody>
                <tr v-for="(v,i) in dataList" :key="`tr_${i}`">
                  <td v-for="(item,index) in tableHeaders" :key="`td_${i}_${index}`"
                    :style="{width: item.width}"
                  >
                    <span v-if="item.value==='action'">
                      <x-loading v-if="item.loading"></x-loading>
                      <button v-else type="button" class="btn btn-link" @click="downloadItemResources(v,i)">下载</button>
                    </span>
                    <span v-else>{{v[item.value]}}</span>
                  </td>
                </tr>
                <tr v-if="dataList.length===0" class="nodata-tr"><td>暂无数据</td></tr>
              </tbody>
            </table>
          </div>
        </fieldset>
      </div>
      <div v-if="loadingCount > 0" class="mask">
        <x-loading></x-loading>
      </div>
    </div>
  </div>
</template>

<script>
const path = global.require('path')
const {remote} = global.require('electron')
const {dialog} = remote
const selfWindow= remote.getCurrentWindow()
let {debounce}  = global.$jstools || {}
let cheerio = global.cheerio || {}
let {downloadFileV3} = global.jstoolsNode || {}

export default {
  name: 'web-crawler',
  data () {
    return {
      urlTemplate: '',
      variableDict: [],
      textDict: {},
      toPath: this.$sharedObject('desktopPath'),
      dataList: [],
      autoSave: false,
      resourcesType: ['img'],
      resourcesTypeList: [
        {label: '图片', value: 'img'},
        {label: '音频', value: 'audio'},
        {label: '视频', value: 'video'},
      ],
      tableHeaders: [
        {label: '源URL', value: 'url', width: '60%'},
        {label: '资源数量', value: 'count', width: '20%'},
        {label: '操作', value: 'action', width: '20%'},
      ],
      thStyles: {},
      loadingCount: 0
    }
  },
  computed: {
    variableArr () {
      let result = []
      let regex = /\$\{(.+?)\}/g
      result = [...new Set(this.urlTemplate.match(regex) || [])]
      return result
    }
  },
  watch: {
    variableArr (n, o) {
      if (n.length < o.length) {
        for (let i = n.length; i < o.length; i++) {
          this.variableDict[i] = ''
        }
      }
    }
  },
  mounted () {
    downloadFileV3 = this.$sharedObject('jstoolsNode').downloadFileV3
  },
  methods: {
    // 设置保存位置
    setToPath () {
      let filePaths = dialog.showOpenDialogSync(selfWindow, {
        title: '选择目录',
        buttonLabel:'确定',
        defaultPath: this.$sharedObject('desktopPath'),
        properties: ['openDirectory']
      })
      console.log(filePaths)
      if (filePaths instanceof Array && filePaths.length > 0) {
        this.toPath = filePaths[0]
      }
    },
    // 整理数据
    getTextDict () {
      let textDict = {res: this.urlTemplate}
      this.variableArr.forEach((v, i) => {
        if (!v) return
        let dictItem = (this.variableDict[i] || '').split(',')
        let _textDict = {}
        dictItem.forEach(v1 => {
          Object.keys(textDict).forEach(k2 => {
            let v2 = textDict[k2] || ''
            _textDict[`${k2}_${v1}`] = v2.replace(v, v1)
          })
        })
        textDict = {..._textDict}
      })
      this.textDict = textDict
    },
    // 修正滚动条
    checkScroll: debounce(function () {
      this.$nextTick(() => {
        if (!this.$refs.table) return
        let tableHeight = this.$refs.table.clientHeight
        let tableParentHeight = this.$refs.table.parentNode.clientHeight
        if (tableHeight > tableParentHeight) {
          this.thStyles = {overflow: 'hidden scroll'}
        } else {
          this.thStyles = {overflow: 'hidden'}
        }
      })
    }),
    clickHandler () {
      if (this.loadingCount > 0) return
      if (this.resourcesType.length === 0) {
        this.$toast('请选择至少一个资源类型')
        return
      }
      this.dataList = []
      this.getTextDict()
      if (Object.keys(this.textDict).length === 0) return
      Object.keys(this.textDict).forEach(k => {
        let item = {
          url: this.textDict[k],
          key: k,
          count: 0,
          resources: [],
          loading: true
        }
        let index = this.dataList.length
        this.dataList.push(item)
        this.getItemResources(item, index)
        this.checkScroll()
      })
    },
    getItemResources (item, index) {
      this.loadingCount++
      this.$jstools.ajax.get(item.url).then((res) => {
        let html = (res || {}).data
        let result = []
        if (html) {
          let $_ = cheerio.load(html)
          this.resourcesType.forEach(v => {
            $_('body').find(v).each((idx, ele) => {
              let src = ele.attribs['src'] || ''
              if (/^\/\//.test(src)) {
                let protocol = /^https/.test(item.url) ? 'https:' : 'http:'
                src = protocol + src
              }
              src && result.push(src)
            })
          })
          this.$set(this.dataList[index], 'resources', result)
          this.$set(this.dataList[index], 'count', result.length)
        }
      }).finally(() => {
        this.$set(this.dataList[index], 'loading', false)
        this.loadingCount--
        if (this.autoSave) {
          this.downloadResources()
        }
      })
    },
    downloadResources () {
      if (this.dataList.length === 0) return Promise.resolve()
      this.loadingCount++
      let taskList = []
      this.dataList.forEach((v, i) => {
        taskList.push(this.downloadItemResources(v, i))
      })
      return Promise.all(taskList).then(() => {
        this.loadingCount--
      })
    },
    downloadItemResources (item, index) {
      if (item.resources.length === 0 || item.loading) return Promise.resolve()
      this.loadingCount++
      this.$set(this.dataList[index], 'loading', true)
      let taskList = []
      item.resources.forEach((url, i) => {
        let arr = url.match(/\b.(gif|jpg|jpeg|png|gif|jpg|png|svg|swf|flv|mp4|rmvb|avi|mpeg|ra|ram|mov|wmv|mp3|wma|mid|midi|wav|vqf)\b/i)
        let ext = (arr || [])[1] ? `.${(arr || [])[1]}` : '.png'
        let fp = path.join(this.toPath, `${item.key}(${i})${ext}`)
        taskList.push(downloadFileV3({from: url, to: fp}))
      })
      return Promise.all(taskList).then(() => {
        this.$set(this.dataList[index], 'loading', false)
        this.loadingCount--
      })
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";
.page-web-crawler {
	display: flex;
	flex-direction: column;
  .row-flex {
    & + .row-flex {
      margin-top: 1em;
    }
    .row-flex-prefix {
      min-width: 4em;
      text-align: right;
    }
    .row-flex-main {
      text-align: left;
    }
  }
	.page-body {
		height: 0;
		flex-grow: 1;
		overflow: hidden auto;
    position: relative;
    fieldset {
      height: 100%;
    }
    .fieldset-content {
      height: calc(100% - 2em);
      display: flex;
      flex-direction: column;
      .main {
        height: 0;
        flex-grow: 1;
        overflow: hidden auto;
      }
      .bottom {
        padding-top: .5em;
      }
    }
    .table-header,
    .table-body {
      table {
        width: 100%;
        border: 1px solid @borderGrey;
        tr {height: 2em;}
        th, td {
          border: 1px solid @borderGrey;
          text-align: center;
          vertical-align: middle;
        }
        .nodata-tr {
          height: 100%;
          font-size: 16px;
          color: #999;
        }
      }
    }
    .table-body {
      height: calc(100% - 4em);
      overflow: auto;
      table {
        min-height: 100%;
      }
    }
    .mask {
      position: absolute;
      left: 15px;
      right: 15px;
      top: 0;
      bottom: 0;
      border-radius: 8px;
      background-color: rgba(255,255,255,.5);
      .flex-center;
    }
  }
}
</style>
